Datetime optimization (#439)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Mon, 20 Jan 2020 17:09:52 +0000 (10:09 -0700)
committerGitHub <noreply@github.com>
Mon, 20 Jan 2020 17:09:52 +0000 (10:09 -0700)
* Optimize performance of DateTime.

Prefer Qt::UTC TimeSpec for storing creation_time.
This avoids very significant performance hits from converting
to/from Qt::LocalTime, which involve DST calculations as well
as the usual offsets.
Test suite with this commit runs at 63% of wall-clock time of the
baseline (time ./testo)!

* remove obsolete fromTime_t and use Qt::UTC.

* Delete obsolete operators and methods of DateTime.

* document motivation for Qt::UTC as default timespec.

Modify Waypoint::SetCreationTime so it can be used with seconds, milliseconds,
or both operands.  This method is implemented without resetting the
Qt::TimeSpec.  It will be efficient if the existing TimeSpec is
Qt::UTC as set up by gpsbabel::DateTime default constructor.

defs.h
garmin_fit.cc
gopal.cc
gtm.cc
lowranceusr.cc
random.cc
src/core/datetime.h
unicsv.cc
util.cc
waypt.cc
xcsv.cc

diff --git a/defs.h b/defs.h
index c906bc458da74a0a015212780d5ea407629da4aa..8c405534776696a62b0d18d71607d4662d9bec16 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -552,8 +552,7 @@ public:
   QString CreationTimeXML() const;
   gpsbabel::DateTime GetCreationTime() const;
   void SetCreationTime(const gpsbabel::DateTime& t);
-  void SetCreationTime(time_t t);
-  void SetCreationTime(time_t t, int ms);
+  void SetCreationTime(qint64 t, qint64 ms = 0);
   geocache_data* AllocGCData();
   int EmptyGCData() const;
 };
index 357484b767be199a168263c9bf74d2fb09d16535..ca55c6ffb149f1e298af96cc8c9999b135de021a 100644 (file)
@@ -779,7 +779,7 @@ fit_parse_data(fit_message_def* def, int time_offset)
     if (alt != 0xffff) {
       waypt->altitude = (alt / 5.0) - 500;
     }
-    waypt->SetCreationTime(QDateTime::fromTime_t(GPS_Math_Gtime_To_Utime(timestamp)));
+    waypt->SetCreationTime(GPS_Math_Gtime_To_Utime(timestamp));
     if (speed != 0xffff) {
       WAYPT_SET(waypt, speed, speed / 1000.0f);
     }
index 31f52b8724d604a714e068bb3460b8bbe7572b1a..c3c74ba9efad6006c10952922f5e86d979725586 100644 (file)
--- a/gopal.cc
+++ b/gopal.cc
@@ -177,9 +177,7 @@ gopal_read()
   
 
   route_head* route = route_head_alloc();
-  QDateTime qtx;
-  qtx.setTimeSpec(Qt::UTC);
-  qtx.setTime_t(tx);
+  QDateTime qtx = QDateTime::fromSecsSinceEpoch(tx, Qt::UTC);
   route->rte_name = "Tracklog ";
   route->rte_name += qtx.toString(Qt::ISODate);
   route_add_head(route);
@@ -290,7 +288,7 @@ gopal_read()
         if (!strptime(c, "%Y%m%d", &tm2)) {
           fatal("Bad date '%s'.\n", c);
         }
-        wpt->creation_time += mkgmtime(&tm2);
+        wpt->creation_time = wpt->creation_time.addSecs(mkgmtime(&tm2));
         break;
       case 10:  // Unknown.  Ignored.
       case 11:  // Bearing.  Ignored.
@@ -335,11 +333,10 @@ gopal_read()
 static void
 gopal_write_waypt(const Waypoint* wpt)
 {
-  char tbuffer[64];
   int fix=fix_unknown;
   //TICK;    TIME;   LONG;     LAT;       HEIGHT; SPEED;  UN; HDOP;     SAT
   //3801444, 080558, 2.944362, 43.262117, 295.28, 0.12964, 2, 2.900000, 3
-  snprintf(tbuffer, sizeof(tbuffer), "%06d", wpt->creation_time.hms());
+  QString tbuffer = wpt->creation_time.toString("HHmmss");
   if (wpt->fix!=fix_unknown) {
     switch (wpt->fix) {
     case fix_none:
@@ -355,7 +352,7 @@ gopal_write_waypt(const Waypoint* wpt)
   }
   //MSVC handles time_t as int64, gcc and mac only int32, so convert it:
   unsigned long timestamp = (unsigned long)wpt->GetCreationTime().toTime_t();
-  gbfprintf(fout, "%lu, %s, %lf, %lf, %5.1lf, %8.5lf, %d, %lf, %d\n",timestamp,tbuffer,  wpt->longitude, wpt->latitude,wpt->altitude,
+  gbfprintf(fout, "%lu, %s, %lf, %lf, %5.1lf, %8.5lf, %d, %lf, %d\n",timestamp, CSTR(tbuffer),  wpt->longitude, wpt->latitude,wpt->altitude,
             wpt->speed,fix,wpt->hdop,wpt->sat);
 }
 
diff --git a/gtm.cc b/gtm.cc
index f417fd630659b84338c15828d070a1b1bd863f4c..1f01751458c5323310e94a38a9e750fd35679b3e 100644 (file)
--- a/gtm.cc
+++ b/gtm.cc
@@ -539,7 +539,7 @@ gtm_read()
     fread_discard(file_in, 1);
     wpt->SetCreationTime(fread_long(file_in));
     if (wpt->creation_time.isValid()) {
-      wpt->creation_time += EPOCH89DIFF;
+      wpt->creation_time = wpt->creation_time.addSecs(EPOCH89DIFF);
     }
     fread_discard(file_in, 2);
     wpt->altitude = fread_single(file_in);
@@ -567,7 +567,7 @@ gtm_read()
     convert_datum(&wpt->latitude, &wpt->longitude);
     wpt->SetCreationTime(fread_long(file_in));
     if (wpt->creation_time.isValid()) {
-      wpt->creation_time += EPOCH89DIFF;
+      wpt->creation_time = wpt->creation_time.addSecs(EPOCH89DIFF);
     }
     start_new = fread_byte(file_in);
     wpt->altitude = fread_single(file_in);
index 6c656854da24fc3a580ff57f15c7f5eba890a57c..6435b5864dc616731f150071ee3cefd70694fee9 100644 (file)
@@ -1537,7 +1537,7 @@ lowranceusr4_parse_trail(int* trail_num)
     gbfgetc(file_in);
 
     /* POSIX timestamp (a.k.a. UNIX Epoch) - seconds since Jan 1, 1970 */
-    wpt_tmp->SetCreationTime(QDateTime::fromTime_t(gbfgetint32(file_in)));
+    wpt_tmp->SetCreationTime(gbfgetint32(file_in));
 
     /* Long/Lat */
     wpt_tmp->longitude = gbfgetdbl(file_in) / DEGREESTORADIANS; /* rad to deg */
index fd14816d31143b6cd0dfca123d2cefda63167cd7..c94b053759f87bc16094acb6e486e38c30953d64 100644 (file)
--- a/random.cc
+++ b/random.cc
@@ -235,7 +235,7 @@ random_read()
 
   route_head* head;
   Waypoint* prev = nullptr;
-  QDateTime time = current_time();
+  QDateTime time = current_time().toUTC();
 
   int points = (opt_points) ? atoi(opt_points) : rand_int(128) + 1;
   if (doing_trks || doing_rtes) {
@@ -286,7 +286,7 @@ random_rd_posn_init(const QString&)
   if (opt_points) {
     realtime->points = atoi(opt_points);
   }
-  realtime->time = current_time();
+  realtime->time = current_time().toUTC();
 }
 
 void
index 7c7eeff7866d2d7bffbd210975b174a94d97315b..0c7581b3ab64e818187b99895f44cfecd48963d6 100644 (file)
@@ -42,38 +42,21 @@ class DateTime : public QDateTime {
 public:
   // As a crutch, mimic the old behaviour of an uninitialized creation time
   // being 1/1/1970.
-  DateTime() {
-    setTime_t(0);
+  // The choice of Qt::TimeSpec here can be critical to performance.
+  // With a Qt::LocalTime conversions between Qt::UTC and Qt::LocalTime
+  // can be required when using a method such as *Epoch, add*.
+  // Using Qt::Utc avoids these conversions.
+  // The lowranceusr regression test was measured taking 2.7x longer,
+  // and the entire regression suite took 1.7x longer, with
+  // Qt::LocalTime compared to Qt::UTC on ubuntu bionic.
+  // Note that these conversions can be required if the Qt::TimeSpec is
+  // set to Qt:LocalTime after construction.
+  DateTime() : QDateTime(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)) {
   }
 
   DateTime(const QDate& date, const QTime& time) : QDateTime(date, time) {}
   DateTime(const QDateTime& dt) : QDateTime(dt) {}
 
-  // TODO: this should go away in favor of .addSecs().
-  // add time_t without losing any existing milliseconds.
-  DateTime& operator+=(const time_t& t) {
-    QDateTime dt = addSecs(t);
-    setDate(dt.date());
-    setTime(dt.time());
-    return *this;
-  }
-
-  // Integer form: YYMMDD
-  int ymd() const {
-    QDate d(date());
-    return d.year() * 10000 + d.month() * 100 + d.day();
-  }
-
-  int ddmmyy() const {
-    QDate d(date());
-    return d.day() * 10000 + d.month() * 100 + d.year();
-  }
-
-  int hms() const {
-    QTime t(time());
-    return t.hour() * 10000 + t.minute() * 100 + t.second();
-  }
-
   // Temporary: Override the standard, also handle time_t 0 as invalid.
   bool isValid() const {
     return date().isValid() && time().isValid() && toTime_t() > 0;
index 2c75836e4a5e10a7eeb4a8861a0e4def736e99fd..b9c2f2654ec2f4968ab9918bfffed62f3438ec5a 100644 (file)
--- a/unicsv.cc
+++ b/unicsv.cc
@@ -468,7 +468,7 @@ unicsv_adjust_time(const time_t time, const time_t* date)
     struct tm tm = *gmtime(&res);
     res = mklocaltime(&tm);
   }
-  return QDateTime::fromTime_t(res);
+  return QDateTime::fromSecsSinceEpoch(res, Qt::UTC);
 }
 
 static bool
@@ -1131,7 +1131,7 @@ unicsv_parse_one_line(const QString& ibuf)
     }
 
     if (opt_utc) {
-      wpt->creation_time += atoi(opt_utc) * SECONDS_PER_HOUR;
+      wpt->creation_time = wpt->creation_time.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
     }
   }
 
@@ -1658,7 +1658,7 @@ unicsv_waypt_disp_cb(const Waypoint* wpt)
         // We might wrap to a different day by overriding the TZ offset.
         dt = dt.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
       } else {
-        dt = wpt->GetCreationTime();
+        dt = wpt->GetCreationTime().toLocalTime();
       }
       QString date = dt.toString("yyyy/MM/dd");
       *fout << unicsv_fieldsep << date;
@@ -1673,7 +1673,7 @@ unicsv_waypt_disp_cb(const Waypoint* wpt)
         t = wpt->GetCreationTime().toUTC().time();
         t = t.addSecs(atoi(opt_utc) * SECONDS_PER_HOUR);
       } else {
-        t = wpt->GetCreationTime().time();
+        t = wpt->GetCreationTime().toLocalTime().time();
       }
       QString out;
       if (t.msec() > 0) {
diff --git a/util.cc b/util.cc
index eae14c61437975da77d5f43a906340aaf282ec68..ded89eefa6a3bc4811153df226a17ac025eae607 100644 (file)
--- a/util.cc
+++ b/util.cc
@@ -735,10 +735,10 @@ gpsbabel::DateTime
 current_time()
 {
   if (gpsbabel_testmode()) {
-    return QDateTime::fromTime_t(0);
+    return QDateTime::fromMSecsSinceEpoch(0, Qt::UTC);
   }
 
-  return QDateTime::currentDateTime();
+  return QDateTime::currentDateTimeUtc();
 }
 
 /*
index 2f8bfb5f26016ec7d78601d211c30416bddf226d..67921e8ac545cc19d62d3dda2530a8050c33cba0 100644 (file)
--- a/waypt.cc
+++ b/waypt.cc
@@ -595,16 +595,9 @@ Waypoint::SetCreationTime(const gpsbabel::DateTime& t)
 }
 
 void
-Waypoint::SetCreationTime(time_t t)
+Waypoint::SetCreationTime(qint64 t, qint64 ms)
 {
-  creation_time = QDateTime::fromTime_t(t);
-}
-
-void
-Waypoint::SetCreationTime(time_t t, int ms)
-{
-  creation_time.setTime_t(t);
-  creation_time = creation_time.addMSecs(ms);
+  creation_time.setMSecsSinceEpoch((t * 1000) + ms);
 }
 
 geocache_data*
diff --git a/xcsv.cc b/xcsv.cc
index 035771358aa2a05506b773c7e4ee2bc4b289e69d..11d9c1ca5ba6b3e328a201a4b21991533c352471 100644 (file)
--- a/xcsv.cc
+++ b/xcsv.cc
@@ -806,7 +806,7 @@ xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp,
   case XT_TIMET_TIME_MS: {
     /* Time as time_t in milliseconds */
     bool ok;
-    wpt->SetCreationTime(QDateTime::fromMSecsSinceEpoch(value.toLongLong(&ok)));
+    wpt->SetCreationTime(0, value.toLongLong(&ok));
     if (!ok) {
       warning("parse of string '%s' on line number %d as TIMET_TIME_MS failed.\n", s, line_no);
     }
@@ -820,17 +820,17 @@ xcsv_parse_val(const QString& value, Waypoint* wpt, const field_map& fmp,
     break;
   case XT_LOCAL_TIME:
     if (!gpsbabel_testmode()) {
-      wpt->creation_time += sscanftime(s, fmp.printfc.constData(), 0);
+      wpt->creation_time = wpt->creation_time.addSecs(sscanftime(s, fmp.printfc.constData(), 0));
     } else {
       /* Force constant time zone for test */
-      wpt->creation_time += sscanftime(s, fmp.printfc.constData(), 1);
+      wpt->creation_time = wpt->creation_time.addSecs(sscanftime(s, fmp.printfc.constData(), 1));
     }
     break;
     /* Useful when time and date are in separate fields
        GMT / Local offset is handled by the two cases above */
   case XT_HMSG_TIME:
   case XT_HMSL_TIME:
-    wpt->creation_time += addhms(s, fmp.printfc.constData());
+    wpt->creation_time = wpt->creation_time.addSecs(addhms(s, fmp.printfc.constData()));
     break;
   case XT_ISO_TIME:
   case XT_ISO_TIME_MS: